package com.doraro.coal.exception.handler;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.doraro.coal.exception.R;
import com.doraro.coal.exception.code.ExceptionCode;
import com.doraro.coal.exception.uncheck.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.List;


/**
 * 处理抛出的异常，用于更好地记录和返回给前端
 */
@ControllerAdvice
@Slf4j
public class RestExceptionHandler {
    /**
     * 其他异常,用户跳转到错误页面
     *
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(HttpServletRequest reqest,
                               HttpServletResponse response, Exception e) throws Exception {
        log.error(e.getMessage(), e);
        if (isAjax(reqest)) {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(R.fail(500, "系统异常，请联系管理员")));
            writer.flush();
            writer.close();
            return null;
        } else {
            ModelAndView mav = new ModelAndView();
            mav.setViewName("error/500");
            return mav;
        }
    }
    /**
     * 判断是否是ajax请求
     */
    public static boolean isAjax(HttpServletRequest httpRequest) {
        return (httpRequest.getHeader("X-Requested-With") != null
                && "XMLHttpRequest"
                .equals(httpRequest.getHeader("X-Requested-With").toString()));
    }
    /**
     * shiro权限异常
     * @param ex
     * @return
     */
    @ExceptionHandler(UnauthorizedException.class)
    public String UnauthorizedException(Exception ex) {
        log.error("Exception:", ex);
        return "error/400";
    }
    /**
     * 处理前端传入参数错误的异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public R httpMessageNotReadableException(HttpMessageNotReadableException ex) {
        log.error("HttpMessageNotReadableException:", ex);
        String message = ex.getMessage();
        if (message != null && !"".equals(message)) {
            if (message.contains("Could not read document:")) {
                String msg = "无法正确的解析json类型的参数：" +
                        message.substring(message.indexOf("Could not read document:") +
                                "Could not read document:".length(), message.indexOf(" at "));
                return R.result(ExceptionCode.PARAM_EX.getCode(), null, msg);
            }
        }
        return R.result(ExceptionCode.PARAM_EX.getCode(), "", ExceptionCode.PARAM_EX.getMsg());
    }

    /**
     * 处理不符合@Valid的异常
     *
     * @param eee
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public R bindException(BindException eee) {
        log.error("BindException:", eee);
        try {
            String msgs = eee.getBindingResult().getFieldError().getDefaultMessage();
            if (StringUtils.isNotEmpty(msgs)) {
                return R.result(ExceptionCode.PARAM_EX.getCode(), null, msgs);
            }
        } catch (Exception ee) {

        }
        StringBuilder msg = new StringBuilder();
        List<FieldError> fieldErrors = eee.getFieldErrors();
        fieldErrors.forEach((oe) ->
                msg.append("参数对象[").append(oe.getObjectName()).append("]的字段[")
                        .append(oe.getField()).append("]的值[").append(oe.getRejectedValue()).append("]与实际类型不匹配.")

        );
        return R.result(ExceptionCode.PARAM_EX.getCode(), null, msg.toString());
    }

    /**
     * 处理不符合@Valid的异常
     *
     * @param eee
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseBody
    public R methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException eee) {
        log.error("MethodArgumentTypeMismatchException:", eee);
        StringBuilder msg = new StringBuilder("参数[").append(eee.getName()).append("]的值[")
                .append(eee.getValue()).append("]与实际类型[").append(eee.getRequiredType().getName()).append("]不匹配");
        return R.result(ExceptionCode.PARAM_EX.getCode(), null, msg.toString());
    }

    /**
     * IllegalStateException
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(IllegalStateException.class)
    public R illegalStateException(IllegalStateException ex) {
        log.error("IllegalStateException:", ex);
        return R.result(ExceptionCode.ILLEGALA_ARGUMENT_EX.getCode(), null, ExceptionCode.ILLEGALA_ARGUMENT_EX.getMsg());
    }

    /**
     * 缺少必须参数的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public R missingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("MissingServletRequestParameterException:", e);
        StringBuilder msg = new StringBuilder();
        msg.append("缺少必须的[").append(e.getParameterType()).append("] 类型的参数[").append(e.getParameterName()).append("]");
        return R.result(ExceptionCode.ILLEGALA_ARGUMENT_EX.getCode(), null, msg.toString());
    }

    /**
     * 空指针异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public R nullPointerException(NullPointerException ex) {
        log.error("NullPointerException:", ex);
        return R.result(ExceptionCode.NULL_POINT_EX.getCode(), null, ExceptionCode.NULL_POINT_EX.getMsg());
    }

    /**
     * 参数不正确异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public R illegalArgumentException(IllegalArgumentException ex) {
        log.error("IllegalArgumentException:", ex);
        return R.result(ExceptionCode.ILLEGALA_ARGUMENT_EX.getCode(), null, ExceptionCode.ILLEGALA_ARGUMENT_EX.getMsg());
    }

    /**
     * content type 不支持异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    public R httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.error("HttpMediaTypeNotSupportedException:", e);
        MediaType contentType = e.getContentType();
        if (contentType != null) {
            StringBuilder msg = new StringBuilder();
            msg.append("请求类型(Content-Type)[").append(contentType.toString()).append("] 与实际接口的请求类型不匹配");
            return R.result(ExceptionCode.MEDIA_TYPE_EX.getCode(), null, msg.toString());
        }
        return R.result(ExceptionCode.MEDIA_TYPE_EX.getCode(), null, "无效的Content-Type类型");
    }

    /**
     * 上传文件异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingServletRequestPartException.class)
    @ResponseBody
    public R missingServletRequestPartException(MissingServletRequestPartException ex) {
        log.error("MissingServletRequestPartException:", ex);
        return R.result(ExceptionCode.REQUIRED_FILE_PARAM_EX.getCode(), null, ExceptionCode.REQUIRED_FILE_PARAM_EX.getMsg());
    }

    /**
     * 上传文件异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ServletException.class)
    @ResponseBody
    public R servletException(ServletException ex) {
        log.error("ServletException:", ex);
        String msg = "UT010016: Not a multi part request";
        if (msg.equalsIgnoreCase(ex.getMessage())) {
            return R.result(ExceptionCode.REQUIRED_FILE_PARAM_EX.getCode(), null, ExceptionCode.REQUIRED_FILE_PARAM_EX.getMsg());
        }
        return R.result(ExceptionCode.SYSTEM_BUSY.getCode(), "", ex.getMessage());
    }

    /**
     * 上传文件异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MultipartException.class)
    @ResponseBody
    public R multipartException(MultipartException ex) {
        log.error("MultipartException:", ex);
        return R.result(ExceptionCode.REQUIRED_FILE_PARAM_EX.getCode(), null, ExceptionCode.REQUIRED_FILE_PARAM_EX.getMsg());
    }


    /**
     * 处理@Valid异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.error("MethodArgumentNotValidException:", ex);
        return R.result(ExceptionCode.BASE_VALID_PARAM.getCode(), "", ex.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * 返回状态码:405
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseBody
    public R<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return R.fail(ExceptionCode.METHOD_NOT_ALLOWED);
    }

    /**
     * shiro 用户错误
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler({AuthenticationException.class})
    public R handlerUnknownAccountException(AuthenticationException ex) {
        return R.fail(404,ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({BizException.class})
    public R handlerBizException(BizException ex) {
        return R.fail(ex.getCode(),ex.getMessage());
    }
}
